package com.example.demo.serviceImpl.task;


import com.example.demo.dao.task.TaskAppDao;
import com.example.demo.dao.task.TaskDao;
import com.example.demo.dao.task.TaskReportDao;
import com.example.demo.dao.user.EmployerTaskDao;
import com.example.demo.dao.user.WorkerTaskDao;
import com.example.demo.po.opt.Opt;
import com.example.demo.po.task.Task;
import com.example.demo.po.user.User;
import com.example.demo.service.app.AppService;
import com.example.demo.service.bug.BugService;
import com.example.demo.service.report.ReportService;
import com.example.demo.service.task.TaskService;
import com.example.demo.service.user.UserService;
import com.example.demo.utils.Constant;
import com.example.demo.utils.FileHelper;
import com.example.demo.utils.MapHelper;
import com.example.demo.utils.wordCountHelper;
import com.example.demo.vo.ResultVO;
import com.example.demo.vo.report.ReportCoNumVO;
import com.example.demo.vo.report.ReportSimNumVO;
import com.example.demo.vo.task.TaskDocVO;
import com.example.demo.vo.task.TaskSimpleVO;
import com.example.demo.vo.task.TaskVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class TaskServiceImpl implements TaskService {
    private static final Logger logger = LoggerFactory.getLogger(TaskServiceImpl.class);
    @Resource
    private wordCountHelper wordCountHelper;
    @Value("${web.doc-upload-path}")
    private String path;
    @Resource
    TaskDao taskDao;
    @Resource
    EmployerTaskDao employerTaskDao;
    @Resource
    WorkerTaskDao workerTaskDao;
    @Resource
    TaskReportDao taskReportDao;
    @Resource
    TaskAppDao taskAppDao;
    @Resource
    UserService userService;
    @Resource
    AppService appService;
    @Resource
    ReportService reportService;
    @Resource
    BugService bugService;



    @Override
    public ResultVO<List<TaskSimpleVO>> getValidTaskSimple(int pageNum) {
        List<TaskSimpleVO> res = new ArrayList<>();
        int count = taskDao.selectValidTaskSimpleCount(LocalDateTime.now());
        for(Task task:
            taskDao.selectValidTaskSimple(LocalDateTime.now(),pageNum,10)){
            checkState(task);
            if(task.getState()==1){
                res.add(new TaskSimpleVO(task));
            }

        }
        return new ResultVO<List<TaskSimpleVO>>(1,pageNum+"/"+((count-1)/10+1),res);
    }

    @Override
    public ResultVO<List<TaskSimpleVO>> getAllTaskSimple(int pageNum) {
        Date now = new Date();
        int count = 0;
        List<TaskSimpleVO> res = new ArrayList<>();
        count = taskDao.selectAllTaskSimpleCount();
        for(Task task:
                taskDao.selectAllTaskSimple(pageNum,10)){
            checkState(task);
            res.add(new TaskSimpleVO(task));
        }
        return new ResultVO<List<TaskSimpleVO>>(1,pageNum+"/"+((count-1)/10+1),res);
    }

    @Override
    public ResultVO<TaskVO> getTaskByTaskId(int tid) {
        if(taskDao.selectTaskById(tid)==null){
            return new ResultVO<TaskVO>(-1,"未找到id为"+tid+"的任务");
        }
        Task task = taskDao.selectTaskById(tid);
        checkState(task);
        TaskVO res = new TaskVO(task);
        res.setAppFile(appService.getAppByTask(tid).getFile());
        res.setAid(appService.getAppByTask(tid).getAid());
        return new ResultVO<TaskVO>(1,"查询成功",res);
    }

    @Override
    public List<TaskVO> getAllValidTaskByUser(String username) {
        List<TaskVO> res = new ArrayList<>();
        List<Task> data = taskDao.selectAllValidTaskByUser(username,LocalDateTime.now());
        for(Task task: data){
            checkState(task);
            if(task.getState()==1){
                res.add(new TaskVO(task));
            }

        }
        return res;
    }

    @Override
    public ResultVO<List<TaskSimpleVO>> getTaskSimpleByWorker(String username,int pageNum) {
        if(userService.getUserByName(username)==null){
            return new ResultVO<List<TaskSimpleVO>>(-1,"用户不存在",null);
        }
        User user = userService.getUserByName(username);
        int count = workerTaskDao.selectTaskSimpleCountByWorker(username);
        if(user.getRole()!=2){
            return new ResultVO<List<TaskSimpleVO>>(-1,"用户不是众包工人",null);
        }
        List<TaskSimpleVO> res=new ArrayList<>();
        for(Task task:
                workerTaskDao.selectTaskSimpleByWorker(username,pageNum,10)){
            checkState(task);
            TaskSimpleVO taskSimpleVO=new TaskSimpleVO(task);
            taskSimpleVO.setTakeState(workerTaskDao.selectTakeTaskStateByWorker(username,task.getTid()));
            res.add(taskSimpleVO);
        }
        return new ResultVO<List<TaskSimpleVO>>(1,pageNum+"/"+((count-1)/10+1),res);
    }



    @Override
    public ResultVO<List<TaskSimpleVO>> getTaskSimpleByEmployer(String username,int pageNum) {
        if(userService.getUserByName(username)==null){
            return new ResultVO<List<TaskSimpleVO>>(-1,"用户不存在",null);
        }
        User user = userService.getUserByName(username);
        int count = employerTaskDao.selectTaskSimpleCountByEmployer(username);
        if(user.getRole()!=1){
            return new ResultVO<List<TaskSimpleVO>>(-1,"用户不是发包方",null);
        }
        List<TaskSimpleVO> res = new ArrayList<>();
        for(Task task:
                employerTaskDao.selectTaskSimpleByEmployer(username,pageNum,10)){
            checkState(task);
            res.add(new TaskSimpleVO(task));
        }
        return new ResultVO<List<TaskSimpleVO>>(1,pageNum+"/"+((count-1)/10+1),res);
    }



    // 维护用户的state，这里对结构做了修改，取消了数据库中的state属性
    private void checkState(Task task){
        LocalDateTime now = LocalDateTime.now();
        if(now.isBefore(task.getStartTime())){
            task.setState(0);
        }else if(now.isAfter(task.getEndTime())){
            task.setState(2);
        }else{
            task.setState(1);
        }
    }

    @Override
    public ResultVO<TaskDocVO> uploadDoc(MultipartFile doc) {
        try {
            TaskDocVO taskDocVO = FileHelper.saveDoc(path, doc);
            return new ResultVO<TaskDocVO>(1, "文档上传成功", taskDocVO);
        } catch (IOException ioException){
            logger.error("文件复制时出错", ioException);
        }
        return new ResultVO<>(-1, "服务器错误，请联系网站管理员。");
    }

    @Override
    public ResultVO<TaskVO> uploadTask(TaskVO taskVO) {
        int a=taskDao.insertTask(taskVO);
        taskAppDao.insertTaskApp(taskVO.getTid(),taskVO.getAid());
        employerTaskDao.insertEmployerTask(userService.getUserByName(taskVO.getUsername()).getUid(),taskVO.getTid());
        return new ResultVO<TaskVO>(a,"状态已返回",taskVO);
    }

    @Override
    public void downloadFile(String originName, String newName, HttpServletResponse response) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        response.setContentType("application/x-msdownload");
        try {
            org.springframework.core.io.Resource resource = FileHelper.loadFileAsResource(path, originName);
            if(resource == null)
                return;
            inputStream = resource.getInputStream();
            //1.设置文件ContentType类型
            response.setContentType("application/octet-stream;charset=UTF-8");
            outputStream = response.getOutputStream();
            //2.转码  UTF_8为传入的newName编码的格式 ISO_8859_1为浏览器默认编码
            String convertName = new String(newName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            //3.设置 header  Content-Disposition
            response.setHeader("Content-Disposition", "attachment; filename=" + convertName);
            int b = 0;
            byte[] buffer = new byte[2048];
            while (b != -1) {
                b = inputStream.read(buffer);
                if (b != -1) {
                    outputStream.write(buffer, 0, b);
                }
            }
        } catch (IOException e) {
            logger.error("文件下载时出错", e);
        } finally {
            try {
                if(inputStream != null)
                    inputStream.close();
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                logger.error("输入流或输出流关闭时出错！", e);
            }
        }
    }

    @Override
    public Task getTaskById(int tid) {
        return taskDao.selectTaskById(tid);
    }

    @Override
    public void updateCurrentWorkerById(int tid) {
        taskDao.updateCurrentWorkerById(tid);
    }

    @Override
    public ResultVO<List<ReportCoNumVO>> getAllCoreportNumByTid(int tid) {
        List<ReportCoNumVO> reportCoNumVOList=new ArrayList<>();
        List<Integer> rids=taskReportDao.selectRidByTid(tid);
        if(rids.size()==0){
            return new ResultVO<>(-1,"查询失败，没有已提交报告",null);
        }
        for(int rid:rids){
            reportCoNumVOList.add(new ReportCoNumVO(rid,reportService.getCoreportNumByRid(rid)));
        }
        return new ResultVO<>(1,"查询成功",reportCoNumVOList);
    }

    @Override
    public ResultVO<List<ReportSimNumVO>> getAllSimilarityNumByTid(int tid, int min, int max) {
        List<ReportSimNumVO> reportSimNumVOList=new ArrayList<>();
        List<Integer> rids=taskReportDao.selectRidByTid(tid);
        if(rids.size()==0){
            return new ResultVO<>(-1,"查询失败，没有已提交报告",null);
        }
        for(int rid:rids){
            reportSimNumVOList.add(new ReportSimNumVO(rid,reportService.getSimilarityNumByRid(rid,min,max)));
        }
        return new ResultVO<>(1,"查询成功",reportSimNumVOList);
    }

    @Override
    public int getDifficultyByTid(int tid) {
        return taskDao.selectDifficultyByTid(tid);
    }

    //推荐工人给任务

    /**
            * 计算经验策略的经验值
     *
             * @param uid 工人id
     * @return the 工人发现的bug数
     */
    int calculateExp(int uid){
        int bugCount = 0;
        List<Integer> reportIds = userService.getReportIdByWorker(uid);
        for(int rid:reportIds){
            bugCount += reportService.getBugNumByReport(rid);
        }
        return bugCount;
    }

    //todo 计算TF值
    int calculateTF(String term,int uid){
        int tfCount=0;
        List<Integer> reportIds=userService.getReportIdByWorker(uid);
        for(int rid:reportIds){
            String allBug= reportService.getAllBugByRid(rid);
            Map<String,Integer> terms = wordCountHelper.countWord(allBug);
            if (terms.containsKey(term)){
                tfCount++;
            }
        }
        return tfCount;
    }
    //todo  计算DF值
    int calculateDF(String term){
        Set<Integer> userIdSet=new HashSet<>();
        List<Opt> opts=bugService.getOpt();
        for(Opt opt:opts){
            Map<String,Integer> terms = wordCountHelper.countWord(opt.getAllBug());
            if(terms.containsKey(term)){
                userIdSet.add(opt.getUid());
            }
        }
        return userIdSet.size();
    }

    //todo 计算P值
    double calculateP(int uid,String term,int sumTF,int sumDF){
        int tf=calculateTF(term,uid);
        int df=calculateDF(term);
        if(sumDF==0){
            return 1;
        }else if(sumTF==0){
            return Constant.epsilon;
        }else if(tf==0){
            return Constant.epsilon;
        }else if(df==0){
            return 1000;
        }
        double p = (double) (tf*sumDF)/(sumTF*df);
        return p;
    }

    double calculateRel(int tid,int uid){
        double rel=1.0;
        int sumTF=0;
        int sumDF=0;
        Map<String,Integer> terms = wordCountHelper.countWord(taskDao.selectTaskById(tid).getIntro());
        for(String term: terms.keySet()){
            sumTF+=calculateTF(term,uid);
            sumDF+=calculateDF(term);
        }
        for(String term: terms.keySet()){
            rel=rel*calculateP(uid,term,sumTF,sumDF);
        }
        return rel;
    }

    //todo 计算p值
    double calculate_p(int tid,String term,List<User> chosenWorker){
        double res=1.0;
        int sumTF=0;
        int sumDF=0;
        Map<String,Integer> terms = wordCountHelper.countWord(taskDao.selectTaskById(tid).getIntro());
        for(String t:terms.keySet()){
            sumDF+=calculateDF(t);
        }
        for(User user:chosenWorker){
            for(String t:terms.keySet()){
                sumTF+=calculateTF(t,user.getUid());
            }
            res = res*(1-sigmoid(calculateP(user.getUid(),term,sumTF,sumDF)));
        }
        return res;
    }

    double calculateDiversity(List<User> chosenWorker,int tid,int uid){
        Map<String,Integer> terms = wordCountHelper.countWord(taskDao.selectTaskById(tid).getIntro());
        double diversity=0.0;
        int sumTF=0;
        int sumDF=0;
        for(String t:terms.keySet()){
            sumDF+=calculateDF(t);
            sumTF+=calculateTF(t,uid);
        }
        for(Map.Entry<String,Integer> term:terms.entrySet()){
            String t = term.getKey();
            diversity+= calculateP(uid,t,sumTF,sumDF)*calculate_p(tid,t,chosenWorker);
        }
        return diversity;
    }

    //判断集合中是否有特定uid的用户
    boolean isContain(List<User> users,int uid ){
        for(User user:users){
            if(user.getUid()==uid){
                return true;
            }
        }
        return false;
    }

    //多样性计算
    public List<User> recommendByDiversity(int tid){
        String des= taskDao.selectTaskById(tid).getIntro();
        Map<String,Integer> terms = wordCountHelper.countWord(des);
        List<User> users = userService.getAllWorkers();
        ArrayList<User> chosenWorker = new ArrayList<>();
        int count = 0;
        int index = 0;
        //随机选择三个工人作为初始工作集
        int number= new Random().nextInt(users.size()-2)+1;
        chosenWorker.add(users.get(number-1));
        chosenWorker.add(users.get(number));
        chosenWorker.add(users.get(number+1));
        //计算多样性并排序
        for(User user:users){
            user.setDiversity(calculateDiversity(chosenWorker,tid,user.getUid()));
        }
        users.sort(new Comparator<User>() {
            @Override
            public int compare(User user, User t1) {
                if (user.getDiversity() < t1.getDiversity()) {
                    return 1;
                }
                ;
                return 0;
            }
        });
        //先将最多样的两个用户丢进选择组
        ArrayList<User> temp=new ArrayList<>();
        while(count<5) {
            if(!isContain(chosenWorker,users.get(index).getUid())){
                temp.add(users.get(index));
                count++;
            }else {
                index++;
            }
        }
        chosenWorker=temp;
        //重复丢直到达到十个人然后返回
        while(chosenWorker.size()<10){
            for(User user:users){
                user.setDiversity(calculateDiversity(chosenWorker,tid,user.getUid()));
            }
            users.sort(new Comparator<User>() {
                @Override
                public int compare(User user, User t1) {
                    if (user.getDiversity() < t1.getDiversity()) {
                        return 1;
                    }
                    ;
                    return 0;
                }
            });
            count=0;
            index=0;
            while(count<5) {
                if(!isContain(chosenWorker,users.get(index).getUid())){
                    chosenWorker.add(users.get(index));
                    count++;
                }else {
                    index++;
                }
            }
        }
        return chosenWorker;
    }

    @Override
    public List<User> recommendByExp(int tid) {
        int userNum=0;
        List<User> users = userService.getAllWorkers();
        Map<User, Integer> userAndExp=new HashMap<>();
        for(User user:users){
            int exp=calculateExp(user.getUid());
            userAndExp.put(user,exp);
        }
        userAndExp= MapHelper.sortDescend(userAndExp);
        List<User> recommendUsers=new ArrayList<>();
        for(User user:userAndExp.keySet()){
            recommendUsers.add(user);
            userNum++;
            if(userNum==10){
                break;
            }
        }
        return recommendUsers;
    }

    @Override
    public List<User> recommendByRel(int tid) {
        int userNum=0;
        List<User> users = userService.getAllWorkers();
        Map<User, Double> userAndRel=new HashMap<>();
        for(User user:users){
            double rel=calculateRel(tid,user.getUid());
            userAndRel.put(user,rel);
        }
        userAndRel= MapHelper.sortDescend(userAndRel);
        List<User> recommendUsers=new ArrayList<>();
        for(User user:userAndRel.keySet()){
            recommendUsers.add(user);
            userNum++;
            if(userNum==10){
                break;
            }
        }
        return recommendUsers;
    }


    public void calculateAll(int tid, List<User> chosenWorker,Map<User,Double> userAndExp,Map<User,Double> userAndRel,Map<User,Double> userAndDiv,Map<User,Double> userAndAll){
        List<User> users = userService.getAllWorkers();
        for(User user:users){
            double exp = calculateExp(user.getUid());
            double rel = calculateRel(tid,user.getUid());
            double div = calculateDiversity(chosenWorker,tid,user.getUid());
            userAndExp.put(user,exp);
            userAndRel.put(user,rel);
            userAndDiv.put(user,div);
        }
        //最大最小值归一化
        maxMinNormalization(userAndExp);
        maxMinNormalization(userAndRel);
        maxMinNormalization(userAndDiv);
        for(Map.Entry<User,Double> entry:userAndExp.entrySet()){
            User user = entry.getKey();
            //权重取自论文
            double allValue = 0.1 * entry.getValue() + 0.5 * userAndRel.get(user) + 0.4 * userAndDiv.get(user);
            userAndAll.put(user,allValue);
        }
    }

    public List<User> recommendAll(int tid){
        int userNum=0;
        List<User> users = userService.getAllWorkers();
        Map<User, Double> userAndRel=new HashMap<>();
        Map<User, Integer> userAndExp=new HashMap<>();
        Map<User, Double> userAndAll = new HashMap<>();
        for(User user:users){
            double rel=calculateRel(tid,user.getUid());
            int exp=calculateExp(user.getUid());
            userAndRel.put(user,rel);
            userAndExp.put(user,exp);
        }
        maxMinNormalization(userAndRel);
        Collection<Integer> collections = userAndExp.values();
        int max = Collections.max(collections);
        int min = Collections.min(collections);
        for(Map.Entry<User,Integer> entry:userAndExp.entrySet()){
            double value=0.2*(double)(entry.getValue()-min)/(max-min)+0.8*userAndRel.get(entry.getKey());
            userAndAll.put(entry.getKey(),value);
        }
        MapHelper.sortDescend(userAndAll);
        List<User> recommendUsers=new ArrayList<>();
        for(User user:userAndAll.keySet()){
            recommendUsers.add(user);
            userNum++;
            if(userNum==10){
                break;
            }
        }
        return recommendUsers;
    }

    @Override
    public List<User> recommendAltogether(int tid){
        ArrayList<User> chosenWorker = new ArrayList<>();
        List<User> users = userService.getAllWorkers();
        Map<User, Double> userAndExp=new HashMap<>();
        Map<User, Double> userAndRel=new HashMap<>();
        Map<User, Double> userAndDiv=new HashMap<>();
        Map<User, Double> userAndAll=new HashMap<>();
        int count = 0;
        //随机选择三个工人作为初始工作集
        int number= new Random().nextInt(users.size()-2)+1;
        chosenWorker.add(users.get(number-1));
        chosenWorker.add(users.get(number));
        chosenWorker.add(users.get(number+1));
        calculateAll(tid,chosenWorker,userAndExp,userAndRel,userAndDiv,userAndAll);
        MapHelper.sortDescend(userAndAll);
        ArrayList<User> temp=new ArrayList<>();
        for(User user:userAndRel.keySet()) {
            while(count<2) {
                if (!isContain(chosenWorker, user.getUid())) {
                    temp.add(user);
                    count++;
                }
            }
            break;
        }
        chosenWorker=temp;
        while(chosenWorker.size()<10){
            calculateAll(tid,chosenWorker,userAndExp,userAndRel,userAndDiv,userAndAll);
            MapHelper.sortDescend(userAndAll);
            count = 0;
            for(User user:userAndRel.keySet()) {
                while(count<2) {
                    if (!isContain(chosenWorker, user.getUid())) {
                        chosenWorker.add(user);
                        count++;
                    }
                }
                break;
            }
        }
        return chosenWorker;
    }

    //最大最小值归一化
    public void maxMinNormalization(Map<User,Double> map){
        Collection<Double> collection = map.values();
        double max = Collections.max(collection);
        double min = Collections.min(collection);
        for(Map.Entry<User,Double> entry:map.entrySet()){
            if(entry.getValue()==min){
                entry.setValue(Constant.epsilon);
            }else if(entry.getValue()==max){
                entry.setValue(1-Constant.epsilon);
            }else {
                double normalizedValue = (entry.getValue() - min) / (max - min);
                entry.setValue(normalizedValue);
            }

        }
    }

    public double sigmoid(double value){
        double ey = Math.pow(Math.E, -value);
        double result = 1 / (1 + ey);
        return result;
    }
}
